﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;
using System.Collections.ObjectModel;
using Sofire.DataComm.Remoting.Server.Settings;
using System.Reflection;
using System.Runtime.InteropServices;
using Sofire.DataComm.Net.Async;
using Sofire.DataComm.Net;

namespace Sofire.DataComm.Remoting.Server
{
    internal partial class RemotingServer : _IServer
    {
        private Dictionary<Socket, RemotingContext> _Clients;
        private readonly object ClientSyncObject = new object();

        private HostSetting _Host;
        private AsyncSocketServer _innerServer;
        private Dictionary<string, ServiceSetting> _ServiceSettings;

        public RemotingServer(HostSetting host)
        {
            this._Host = host;
            var endPoint = new IPEndPoint(host.Address, host.Port);
            this._Clients = new Dictionary<Socket, RemotingContext>(host.MaxConnectionCount);
            this._ServiceSettings = new Dictionary<string, ServiceSetting>(host.Count);
            foreach(var service in host) this._ServiceSettings.Add(service.FullName, service);
            this._innerServer = new AsyncSocketServer(endPoint.ToString(), new SocketInfo(ProtocolMode.Tcp, endPoint), host.MaxConnectionCount, host.MaxReceiveBufferSize, host.ListenBacklog);
            this._innerServer.StateChanged += new CommunicationStateEventHandler(_innerServer_StateChanged);
            this._innerServer.SocketConnected += new AsyncSocketServerEventHandler(InnerServer_SocketConnected);
            this._innerServer.AsyncReceive += new AsyncSocketReceiveEventHandler(InnerServer_AsyncReceive);
            this._innerServer.SocketDisconnected += new AsyncSocketServerEventHandler(InnerServer_SocketDisconnected);
        }

        void _innerServer_StateChanged(object sender, CommunicationStateEventArgs e)
        {
            RemotingApplication.RaiseServerStateChanged(this, e);
        }

        private void SendClient(Socket client, RemotingResult result)
        {
            this._innerServer.SendTo(client, RemotingShare.Formatter.FastWriteBytes(result));
        }

        private void StartThread(ParameterizedThreadStart ev, object state)
        {
            Thread t = new Thread(ev);
            t.IsBackground = true;
            t.Start(state);
            //ThreadPool.QueueUserWorkItem(ev, state);
        }

        #region IServer 成员

        public HostSetting Host
        {
            get
            {
                return this._Host;
            }
        }

        public EndPoint EndPoint
        {
            get { return this._innerServer.SocketInfo.EndPoint; }
        }

        public IEnumerable<ServiceSetting> Services
        {
            get { return this._ServiceSettings.Values; }
        }

        public IEnumerable<RemotingContext> GetActivingClients()
        {
            lock(this.ClientSyncObject)
            {
                return this._Clients.Values;
            }
        }

        #endregion

        #region ICommunicationState 成员

        public CommunicationState State
        {
            get { return this._innerServer.State; }
        }

        public event CommunicationStateEventHandler StateChanged
        {
            add
            {
                this._innerServer.StateChanged += value;
            }
            remove
            {
                this._innerServer.StateChanged -= value;
            }
        }

        #endregion

        #region ICommunication 成员

        public void Open()
        {
            if(_innerServer.IsRunning) return;
            this._innerServer.Open();
        }

        public void Close()
        {
            if(!_innerServer.IsRunning) return;
            this._innerServer.Close();
            foreach(var item in this._Clients)
            {
                item.Key.Close();
            }
            foreach(var service in this._ServiceSettings.Values) service.ResetInstances();
        }

        #endregion

        #region IDisposable 成员

        public void Dispose()
        {
            this.Close();
        }

        #endregion
    }
    internal partial class RemotingServer
    {
        //- 1、客户端成功接入
        void InnerServer_SocketConnected(object sender, AsyncSocketServerEventArgs e)
        {
            //- 接入时预先保留位子
            //lock(ClientSyncObject)
            //{
            //    var socket = e.AsyncEventArgs.AcceptSocket;
            //    this._Clients.Add(socket, null);
            //}
        }

        //- 2、接收客户端数据
        void InnerServer_AsyncReceive(object sender, AsyncSocketReceiveEventArgs e)
        {
            var remoteArgs = e.GetEntity<RemotingArguments>();
            var client = e.AsyncEventArgs.AcceptSocket;
            var args = new ClientRemotingArguments(client, remoteArgs, e);

            RemotingContext context;
            context = args.EventArgs.Tag as RemotingContext;
            if(context == null)
            {

                if((context = args.EventArgs.Tag as RemotingContext) == null)
                {
                    context = this.CreateContext(args);
                    lock(ClientSyncObject)
                    {
                        this._Clients[client] = context;
                    }
                    args.EventArgs.Tag = context;
                    return;
                }

            }
         
            this.StartThread((o) =>
            {
                this.InvokeMethod(args, context);
            }, null);
        }

        //- 3、创建远程上下文
        private RemotingContext CreateContext(ClientRemotingArguments args)
        {
            RemotingContext context = new RemotingContext(this, args.Client);
            var cert = args.Arguments.Certificate;
            context._Certificate = cert;
            var token = args.Arguments.Token;
            ServiceSetting serviceSetting = null;

            //- 判断是否存在短地址（Host/Service.v）的服务配置信息
            if(this._ServiceSettings.TryGetValue(args.Arguments.Certificate.Address, out serviceSetting))
            {
                //- 判断契约信息是否匹配
                if(cert.ContractName == serviceSetting.Contract.FullName)
                {
                    string sessionID = cert.SessionID;
                    bool newSession = string.IsNullOrEmpty(sessionID); //- 判断是否为一个新的会话
                    if(newSession)
                        sessionID = Guid.NewGuid().ToString();
                    else
                        newSession = !serviceSetting.SessionExists(sessionID);

                    context._ServiceSetting = serviceSetting;
                    context._SessionID = sessionID;
                    context._SessionEntry = serviceSetting[sessionID];

                    //- 新会话才需要验证用户
                    if(newSession)
                    {
                        RemotingApplication.RaiseClientJoined(this, context);
                    }
                    //- 自定义验证用户操作
                    if(!newSession || context.IsValidUser)
                    {
                        byte[] methodsBytes = null;
                        //- 服务版本不一致，或者这是一个新会话
                        if(newSession || (int)args.Arguments.Parameters[0] != serviceSetting.Version)
                        {
                            methodsBytes = serviceSetting.MethodSyncInfosBytes;
                        }
                        this.SendClient(args.Client, new RemotingResult(token, new RemotingLoginResultValue(serviceSetting.Version, sessionID, methodsBytes)));
                        return context;
                    }
                    else this.SendClient(args.Client, new RemotingResult(token, ErrorCode.InvalidCertificate, true));
                }
                else this.SendClient(args.Client, new RemotingResult(token, ErrorCode.UnfoundContract, true));
            }
            else this.SendClient(args.Client, new RemotingResult(token, ErrorCode.InvalidAddress, true));

            //RemotingApplication.RaiseClientQuitted(this);

            return null;
        }

        //- 4、调用方法
        private void InvokeMethod(ClientRemotingArguments args, RemotingContext context)
        {
            var client = args.Client;

            RemotingContext._Current = context;
            int methodHashCode = args.Arguments.MethodIdent;
            ContractMethodInfo methodInfo;
            var serviceSetting = context.ServiceSetting;
            try
            {
                RemotingResult result = new RemotingResult(args.Arguments.Token);
                if(serviceSetting.ContractMethods.TryGetValue(methodHashCode, out methodInfo))
                {
                    var method = methodInfo.Method;
                    var isByRef = methodInfo.RefParameters;

                    var parameters = args.Arguments.Parameters;

                    context.SetLastInvoke(method, parameters);

                    RemotingApplication.RaiseClientInvoking(this);

                    object[] returnParameters;
                    try
                    {
                        if(!context.IsValidUser)
                        {
                            result.ErrorCode = ErrorCode.InvalidCertificate;
                        }
                        else
                        {
                            object returnValue = methodInfo.Handler(serviceSetting.FindInstance(this), parameters);
                            returnParameters = new object[1 + parameters.Length];
                            returnParameters[0] = returnValue;
                            for(int i = 0 ; i < parameters.Length ; i++)
                                returnParameters[i + 1] = isByRef[i] ? parameters[i] : null;
                            result.Value = returnParameters;
                            RemotingApplication.RaiseClientInvoked(this, result);
                        }
                    }
                    catch(Exception ex)
                    {
                        if(RemotingApplication.RaiseClientFailed(this, ex))
                        {
                            result.IsTerminated = true;
                        }
                        if(this._Host.ReturnsError) result.Exception = ex;
                        else result.ErrorCode = ErrorCode.DefaultError;
                    }
                }
                else
                {
                    result.ErrorCode = ErrorCode.UnfoundMethod;
                }
                this.SendClient(client, result);
            }
            finally { context.CleatActivingInvoke(); }
        }

        void InnerServer_SocketDisconnected(object sender, AsyncSocketServerEventArgs e)
        {
            RemotingContext context;
            var client = e.AsyncEventArgs.AcceptSocket;

            lock(ClientSyncObject)
            {
                if(!this._Clients.TryGetValue(client, out context)) return;
            }

            if(context != null) RemotingApplication.RaiseClientQuitted(this, context);
            lock(ClientSyncObject)
            {
                this._Clients.Remove(client);
            }
        }
    }
}
